﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Text.Editor.DragDrop;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Operations;
using System.Diagnostics;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using FunctionPoint.Common;
using FunctionPoint.UIManagers;
using Microsoft.VisualStudio.Shell.Interop;
using FunctionPoint.Services;
using FunctionPoint.UI;
using System.Windows.Input;
using Microsoft.VisualStudio.Text;
using System.Collections.ObjectModel;
using FunctionPoint.Adornments;
using EnvDTE;
using System.Management;
//using FunctionPoint.UIManagers;

namespace FunctionPoint
{
    public class WorkItemDropHandler : DropHandlerBase
    {

        internal IDropTarget _dropTarget = null;
        private EnvDTE80.DTE2 _dte2 = null;
        private WorkItem currentWorkItem;
        private IFunctionPointManagerService _managementService = null;


        public IFunctionPointManagerService ManagementService
        {
            get
            {
                if (_managementService == null)
                    _managementService = ServiceProvider.GlobalProvider.GetService(typeof(SFunctionPointManagerService)) as IFunctionPointManagerService;

                return _managementService;
            }
        }
        internal EnvDTE80.DTE2 DTE2
        {
            get
            {
                if (_dte2 == null)
                    _dte2 = ServiceProvider.GlobalProvider.GetService(typeof(SDTE)) as EnvDTE80.DTE2;

                return _dte2;
            }
        }
        public WorkItemDropHandler(IWpfTextView wpfTextView, IEditorOperations editorOptions)
            : base(wpfTextView, editorOptions)
        {
            _dropTarget = Package.GetGlobalService(typeof(SVsMainWindowDropTarget)) as IDropTarget;

            if (_dropTarget != null)
                Debug.WriteLine("WorkItemDropHandler Constructor, Drop Target:", _dropTarget.ToString());
            else 
                Debug.WriteLine("WorkItemDropHandler Constructor, Drop Target:Null");
        }

        #region DropHandlerBase overrides

        public override void HandleDragCanceled()
        {
            Debug.WriteLine("WorkItemDropHandler:HandleDragCanceled");
            currentWorkItem = null;
            base.HandleDragCanceled();
        }
        protected override bool InsertText(Microsoft.VisualStudio.Text.VirtualSnapshotPoint position, string data)
        {
            // we actually do not want to insert text.  We are putting an adornment on the view instead (and perhaps a margin glyph)
            Debug.WriteLine("WorkItemDropHandler:InsertText");
            return true;
        }
        public override bool IsDropEnabled(DragDropInfo dragDropInfo)
        {
            Debug.WriteLine("WorkItemDropHandler:IsDropEnabled, DropTargetNull:", _dropTarget == null);
            return (_dropTarget != null);
        }
        public override DragDropPointerEffects HandleDragStarted(DragDropInfo dragDropInfo)
        {
            Debug.WriteLine("WorkItemDropHandler:HandleDragStarted");
            SetWorkItem(dragDropInfo);

            return base.HandleDragStarted(dragDropInfo);
        }
        public override DragDropPointerEffects HandleDraggingOver(DragDropInfo dragDropInfo)
        {
            Debug.WriteLine("WorkItemDropHandler:HandleDraggingOver");
            return base.HandleDraggingOver(dragDropInfo);
        }
        public override DragDropPointerEffects HandleDataDropped(DragDropInfo dragDropInfo)
        {
            //if (currentWorkItem != null)
            //  ManagementService.CreateDesignPointWorkItem(
            //      ManagementService.AddFunctionPointAdornment(dragDropInfo.VirtualBufferPosition.Position.Position, currentWorkItem));

            // Now we need our design window.  Based on it, we will either cancelOrConfirm the DragDropHandler
            Debug.WriteLine("WorkItemDropHandler:HandleDataDropped");
            
            if (ShowDetailsWindow(dragDropInfo.VirtualBufferPosition.Position.Position, currentWorkItem))
                return base.HandleDataDropped(dragDropInfo);

            return DragDropPointerEffects.None;


        }
        protected override DragDropPointerEffects GetDragDropEffect(DragDropInfo dragDropInfo)
        {
            Debug.WriteLine("WorkItemDropHandler:GetDragDropEffect");
            return base.GetDragDropEffect(dragDropInfo);
        }
        protected override string ExtractText(DragDropInfo dragDropInfo)
        {
            Debug.WriteLine("WorkItemDropHandler:ExtractText");
            if (currentWorkItem != null)
                return currentWorkItem.Title;


            return dragDropInfo.ToString();
        }
        protected override void PerformPostEditActions(DragDropInfo dragDropInfo, bool successfulEdit)
        {

            Debug.WriteLine("WorkItemDropHandler:PerformPostEditActions");
            if (successfulEdit)
                currentWorkItem = null;

            return;
        }
        protected override void PerformPreEditActions(DragDropInfo dragDropInfo)
        {
            Debug.WriteLine("WorkItemDropHandler:PerformPreEditActions");

            return;
        }

        #endregion

        private void SetWorkItem(DragDropInfo dragDropInfo)
        {
            if (dragDropInfo.Data.GetDataPresent(FunctionPoint.Common.Constants.DROPHANDLER_DROPFORMAT_WORKITEMTYPE))
                currentWorkItem = (WorkItem)dragDropInfo.Data.GetData(FunctionPoint.Common.Constants.DROPHANDLER_DROPFORMAT_WORKITEMTYPE);

        }

        private bool ShowDetailsWindow(int bufferPosition, WorkItem workItem)
        {
            var fp = new WorkItemAdornmentHost(workItem, TextView, TextView.TextSnapshot);

            var line = TextView.GetTextViewLineContainingBufferPosition(new SnapshotPoint(TextView.TextSnapshot, bufferPosition));

            DesignPointEditorHost editorHost = new DesignPointEditorHost();

            var editorPoint = Mouse.GetPosition(TextView.VisualElement);

            editorHost.Left = Math.Abs(editorPoint.X);
            editorHost.Top = Math.Abs(editorPoint.Y);

            ViewModels.DesignPointViewModel editorViewModel = new ViewModels.DesignPointViewModel();

            string localPath = TextView.GetFilePath();

            string serverPath = ManagementService.GetServerPath(localPath);
            int changesetId = ManagementService.GetLatestChangeSetId(localPath);
            EnvDTE.ProjectItem projectItem = DTE2.Solution.FindProjectItem(localPath);

            // discover code element
            CodeDiscovery(editorViewModel);

            editorViewModel.SccChangeSet = changesetId;
            editorViewModel.FileName = serverPath;
            editorViewModel.LineNumber = line.Start.GetContainingLine().LineNumber;
            editorViewModel.ClosestCharacterPosition = bufferPosition;
            editorViewModel.Designer = ManagementService.GetCurrentUser();
            editorViewModel.ClosestCharacterPosition = line.Start.Position;
            editorViewModel.DesignerList = new ObservableCollection<TfsUser>(ManagementService.GetUsersByGroupName("Solution Design"));
            editorViewModel.DesignPointName = workItem.FunctionPointTitle;


            editorHost.DataContext = editorViewModel;

            if (editorHost.ShowDialog().GetValueOrDefault(true))
            {
                CodeElementDetails codeElementDetails = new CodeElementDetails()
                    {
                        Method = editorViewModel.MethodName,
                        Class = editorViewModel.ClassName,
                        Namespace = editorViewModel.Namespace
                    };

                var designPointWorkItem = ManagementService.CreateDesignPointWorkItem(editorViewModel.DesignPointName,
                     editorViewModel.Comments, editorViewModel.Designer.DisplayName, workItem.WorkItemId, editorViewModel.FileName, editorViewModel.SccChangeSet,
                      editorViewModel.LineNumber, editorViewModel.ClosestCharacterPosition, codeElementDetails);
                

            }
            return true;
        }
        #region Code Discovery
        /// <summary>
        /// Returns an adjusted CodeElement. Walks comments 'down' to the next real element.
        /// </summary>
        /// <remarks>Courtesy Rick Strahl:
        /// http://www.west-wind.com/weblog/posts/2005/Nov/03/Retrieving-a-CodeElement-reliably-in-VSNET-2005
        /// Modified to work with VS 2010 a bit better.  wasn't using TextPoint, which is apparently critical to accuracy.
        /// </remarks>
        /// <returns></returns>
        private void CodeDiscovery(ViewModels.DesignPointViewModel viewModel)
        {

            EnvDTE.ProjectItem projectItem = DTE2.ActiveDocument.ProjectItem;
            EnvDTE.FileCodeModel codeModel = projectItem.FileCodeModel as EnvDTE.FileCodeModel;
            EnvDTE80.FileCodeModel2 codeModel2 = projectItem.FileCodeModel as EnvDTE80.FileCodeModel2;

            EnvDTE.TextSelection selection = (EnvDTE.TextSelection)projectItem.Document.Selection;

            EnvDTE.vsCMElement scopes = 0;

            try
            {
                var codeElements = codeModel.CodeElements;
                TextPoint txtPoint = selection.ActivePoint as TextPoint;

                foreach (EnvDTE.vsCMElement scope in Enum.GetValues(scopes.GetType()))
                {
                    try
                    {
                        var codeElement = txtPoint.get_CodeElement(scope);

                        if (codeElement != null)
                        {
                            Debug.WriteLine("CodeElement, Kind:{1},  {0}", new object[] { codeElement.FullName, codeElement.Kind });
                            switch (codeElement.Kind)
                            {
                                case vsCMElement.vsCMElementFunction:
                                    viewModel.MethodName = codeElement.FullName;
                                    break;
                                case vsCMElement.vsCMElementClass:
                                    viewModel.ClassName = codeElement.FullName;
                                    break;
                                case vsCMElement.vsCMElementNamespace:
                                    viewModel.Namespace = codeElement.FullName;
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    catch (Exception tEx)
                    {
                        Debug.WriteLine("Exception in Code Element Loop: {0}, \r\nStacktrace:{1}", tEx.Message, tEx.StackTrace);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception..." + ex.Message + "\r\n" + ex.StackTrace);
            }



        }

        #endregion
    }
}
